package com.xgk.boot.module.core.logistics.service;

import com.xgk.boot.framework.common.constant.ElmConstant;
import com.xgk.boot.framework.common.constant.ErrorCodeConstants;
import com.xgk.boot.framework.common.context.ElmOrderThreadContext;
import com.xgk.boot.framework.common.util.date.DateUtils;
import com.xgk.boot.framework.common.util.object.BeanUtils;
import com.xgk.boot.framework.security.core.util.SecurityFrameworkUtils;
import com.xgk.boot.module.core.dal.entity.customer.CustomerBalanceDO;
import com.xgk.boot.module.core.dal.entity.truck.*;
import com.xgk.boot.module.core.enums.biz.*;
import com.xgk.boot.framework.common.exception.util.ServiceExceptionUtil;
import com.xgk.boot.module.core.controller.admin.price.vo.PriceRuleVO;
import com.xgk.boot.module.core.controller.admin.product.vo.product.ProductInfoVO;
import com.xgk.boot.module.core.dal.dto.TruckOrderDto;
import com.xgk.boot.module.core.logistics.service.mothership.dto.OrderBolDto;
import com.xgk.boot.module.core.logistics.service.mothership.dto.OrderQuotationDto;
import com.xgk.boot.module.core.logistics.service.mothership.dto.OrderTrackDto;
import com.xgk.boot.module.core.logistics.service.mothership.response.OrderDto;
import com.xgk.boot.module.core.service.customer.CustomerBalanceServiceImpl;
import com.xgk.boot.module.core.service.product.ProductServiceImpl;
import com.xgk.boot.module.core.service.truck.*;
import com.xgk.boot.module.core.util.price.PriceCalcUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * 订单处理
 * 主要提供一些基于事务的订单操作函数，不依赖其他servcie,避免@Transactional不生效的情况
 */
@Slf4j
@Service
public class OrderHandlerService {

    @Resource
    private ProductServiceImpl productService;
    @Resource
    private TruckOrdersServiceImpl truckOrdersService;
    @Resource
    private TruckOrderServiceFeeServiceImpl truckOrderServiceFeeService;
    @Resource
    private TruckOrderFeeDetailServiceImpl orderFeeDetailService;
    @Resource
    private TruckOrderLogServiceImpl orderLogService;
    @Resource
    private CustomerBalanceServiceImpl customerBalanceService;
    @Resource
    private TruckOrderTrackServiceImpl truckOrderTrackService;
    @Resource
    private TruckOrderBolServiceImpl truckOrderBolService;


    /**
     * todo: 下单前置费用检查
     *  1、获取订单的预扣金额;
     *       2、将客户  充值余额+授信余额+信用余额-冻结金额 累计作为客户总余额;
     *       3、客户总余额大于等于预扣金额，直接下部流程，进行预扣;
     *       4、客户总余额小于预扣金额，返回下单失败，金额不足。
     *       5、余额不足时，同时触发通知消息--告知客户和管理员余额不足。
     * @param truckOrdersDO
     * @return
     */
    @Transactional
    public boolean preCreatOrderFeeCheck(TruckOrdersDO truckOrdersDO, OrderQuotationDto quotationDto) {
        //判断余额是否足够：
        CustomerBalanceDO one = customerBalanceService.getCustBalanceByCustCode(truckOrdersDO.getCustomerCode());
        if (one == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CUSTOMER_BALANCE_NOT_EXISTS, truckOrdersDO.getCustomerCode());
        }
        BigDecimal subtract = one.getCbValue().subtract(one.getCbHoldValue());
        if (subtract.compareTo(quotationDto.getEstimatedPrice()) <= 0) {
            log.error("订单创建前置费用检查失败,费用不足,{}", truckOrdersDO.getOrderCode());
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, "Insufficient Balance for order creation, please recharge your account.");
        }
        //1、锁定费用
        customerBalanceService.addCustomeLockingFees(quotationDto.getEstimatedPrice(), truckOrdersDO);
        return true;
    }

    /**
     * 撤销费用预扣
     * 触发可能点：1、下单失败，释放锁定； 2、下单成功，撤销预扣
     * @param truckOrdersDO
     * @param quotationDto
     */
    public void rebackPreCreatOrderFeeCheck(TruckOrdersDO truckOrdersDO, OrderQuotationDto quotationDto, String msg) {
        customerBalanceService.reduceCustomeLockingFees(quotationDto.getEstimatedPrice(), truckOrdersDO,msg);
    }

    /**
     * 下单完成后后置操作，跟新订单状态，回填订单信息等
     *
     * @param orderInfo
     * @param orderDto
     * @return
     */
    @Transactional
    public void afterCreatOrderSuccessHandler(TruckOrderDto orderInfo, OrderDto orderDto) {
        TruckOrdersDO truckOrdersDO = orderInfo.getTruckOrdersDO();


        // 2-更新订单状态
        TruckOrdersDO byId = truckOrdersService.getById(truckOrdersDO.getOrderId());
        //跟踪号
        byId.setTrackingNumber(orderDto.getId());
        //跟踪地址
        byId.setTrackingUrl(orderDto.getTrackingUrl());
        //物流状态
        LogisticsStatusEnum logisticsStatusEnum = LogisticsStatusEnum.valueOf(orderDto.getStatus());
        if(logisticsStatusEnum == null) {
            logisticsStatusEnum = LogisticsStatusEnum.Unallocated;
        }
        if(byId.getSyncCount() == null) {
            byId.setSyncCount(1);
        } else {
            byId.setSyncCount(byId.getSyncCount() + 1);
        }
        byId.setSyncServiceStatus(SyncStatusEnum.SYNC.getValue());
        //物流状态
        byId.setLogisticsStatus(logisticsStatusEnum.getValue());

        byId.setEarliestPickupDate(DateUtils.dateFormat(orderDto.getEarliestPickupDate()));
        byId.setEstimatedDeliveryDate(DateUtils.dateFormat(orderDto.getEstimatedDeliveryDate()));
        //货币类型
        byId.setCurrencyCode(CurrencyEnum.USD.getValue());
        if(orderDto.getPrice().doubleValue() <= 0) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_CREATE_ORDER_EXCEPTION, "create order price error");
        }
        //订单金额
        byId.setShippingFeeEstimate(calculationOrderPrice(truckOrdersDO, orderDto.getPrice()));
        //订单状态
        byId.setOrderStatus(OrderStatusEnum.PRE_TRANSIT.getValue());
        truckOrdersService.saveOrUpdate(byId);

        // 3-创建订单账单记录
        saveTruckOrderBill(byId, orderDto, "create order");

        //4、费用减扣
        customerBalanceService.reduceCustomeFees(byId.getShippingFeeEstimate(),byId,"create order");

        //1、记录服务商价格信息
        saveTruckOrderServiceFee(truckOrdersDO.getOrderCode(), orderDto,byId.getShippingFeeEstimate());

        log.info("订单同步服务商成功，订单号: {}, 状态: {}", truckOrdersDO.getOrderCode(), orderDto.getStatus());
        //5、记录订单状态变更日志
        addTruckOrderLog(truckOrdersDO, byId, "submit","create order success");
    }


    /**
     * 同服务商同步订单内容（物流状态、物流费用、等）     *
     * @param orderInfo
     * @param orderDto
     */
    @Transactional
    public void afterGetOrderSuccessHandler(TruckOrderDto orderInfo, OrderDto orderDto) {
        TruckOrdersDO truckOrdersDO = orderInfo.getTruckOrdersDO();

        // 1-更新订单状态 跟新对应商品code，spcode,
        TruckOrdersDO byId = truckOrdersService.getById(truckOrdersDO.getOrderId());

        //跟踪地址
        byId.setTrackingUrl(orderDto.getTrackingUrl());
        //物流状态计算
        Integer oldOrderStatus = byId.getOrderStatus();
        LogisticsStatusEnum logisticsStatusEnum = LogisticsStatusEnum.valueOf(orderDto.getStatus());
        if(logisticsStatusEnum == null) {
            logisticsStatusEnum = LogisticsStatusEnum.Unallocated;
        }
        //物流状态
        byId.setLogisticsStatus(logisticsStatusEnum.getValue());

       if(logisticsStatusEnum.equals(LogisticsStatusEnum.Unallocated)){
           byId.setOrderStatus(OrderStatusEnum.PRE_TRANSIT.getValue());
       }else if(logisticsStatusEnum.equals(LogisticsStatusEnum.Completed)){
           byId.setOrderStatus(OrderStatusEnum.FINISHED.getValue());
       } else if (logisticsStatusEnum.equals(LogisticsStatusEnum.Cancelled)) {
           byId.setInterceptStatus(InterceptionStatusEnum.BLOCKED.getValue());
       }else {
           byId.setOrderStatus(OrderStatusEnum.IN_TRANSIT.getValue());
       }

        if(byId.getSyncCount() == null) {
            byId.setSyncCount(1);
        } else {
            byId.setSyncCount(byId.getSyncCount() + 1);
        }
        //订单状态
        truckOrdersService.saveOrUpdate(byId);

        // 记录订单日志等
        log.info("订单服务商获取详情，订单号: {}, 状态: {}", truckOrdersDO.getOrderCode(), orderDto.getStatus());
        //2、记录订单状态变更日志
        if(oldOrderStatus.equals(byId.getOrderStatus())){
            return;
        }
        addTruckOrderLog(truckOrdersDO, byId, "change","get order detail from service");
    }

    /**
     * 下单失败后置操作
     *
     * @param orderInfo
     * @param errorMsg
     */
    @Transactional
    public void afterCreatOrderFailedHandler(TruckOrderDto orderInfo, String errorMsg) {
        //1、将下单异常信息写入主表
        TruckOrdersDO truckOrdersDO = orderInfo.getTruckOrdersDO();
        TruckOrdersDO byId = truckOrdersService.getById(truckOrdersDO.getOrderId());
        if(byId.getSyncCount() == null) {
            byId.setSyncCount(1);
        } else {
            byId.setSyncCount(byId.getSyncCount() + 1);
        }
        byId.setOrderStatus(OrderStatusEnum.ABNORMAL_FORECAST.getValue());
        if(errorMsg.length() > 450) {
            errorMsg = errorMsg.substring(0, 450);
        }
        byId.setSyncInfo(errorMsg);
        byId.setSyncServiceStatus(SyncStatusEnum.SYNC_FAILED.getValue());

        truckOrdersService.saveOrUpdate(byId);
        log.info("订单同步服务商失败，订单号: {}, errormsg: {}", truckOrdersDO.getOrderCode(), errorMsg);
        //2、记录订单操作日志
        addTruckOrderLog(truckOrdersDO, byId, "submit","create order failed:" + errorMsg);
    }


    /**
     * 获取订单物流信息成功后置处理
     * @param orderInfo
     * @param trackingDtoList
     */
    @Transactional
    public void afterGetOrderTrackingSuccessHandler(TruckOrderDto orderInfo, List<OrderTrackDto> trackingDtoList) {
        //1、将下单异常信息写入主表
        TruckOrdersDO truckOrdersDO = orderInfo.getTruckOrdersDO();
        // 1-更新订单状态 跟新对应商品code，spcode,
        TruckOrdersDO byId = truckOrdersService.getById(truckOrdersDO.getOrderId());
        //记录订单物流信息：
        Boolean isFinished = false;
        List<TruckOrderTrackDO> orderTrackDOList = new ArrayList<>();
        for(OrderTrackDto orderTrackDto : trackingDtoList) {
            TruckOrderTrackDO bean = BeanUtils.toBean(orderTrackDto, TruckOrderTrackDO.class);
            bean.setOrderCode(truckOrdersDO.getOrderCode());
            orderTrackDOList.add(bean);
            if(bean.getStatus().equals(LogisticsStatusEnum.Completed.getName())) {
                isFinished = true;
            }
        }
        truckOrderTrackService.saveBatch(orderTrackDOList);

        //跟踪地址
        if(byId.getSyncCount() == null) {
            byId.setSyncCount(1);
        } else {
            byId.setSyncCount(byId.getSyncCount() + 1);
        }
        if(isFinished){
            byId.setOrderStatus(OrderStatusEnum.FINISHED.getValue());
        }
        //订单状态
        truckOrdersService.saveOrUpdate(byId);
            // 记录订单日志等
        log.info("更新订单物流详情，订单号: {}", truckOrdersDO.getOrderCode());
        //2、记录订单状态变更日志
        addTruckOrderLog(truckOrdersDO, byId, "change","get order tracking from service");
    }

    /**
     * 获取订单提单信息成功后置处理
     * @param orderInfo
     * @param orderBolDtoList 提单信息
     */
    @Transactional
    public void afterGetOrderBolsSuccessHandler(TruckOrderDto orderInfo, List<OrderBolDto> orderBolDtoList) {
        //1、将下单异常信息写入主表
        TruckOrdersDO truckOrdersDO = orderInfo.getTruckOrdersDO();
        // 1-更新订单状态 跟新对应商品code，spcode,
        TruckOrdersDO byId = truckOrdersService.getById(truckOrdersDO.getOrderId());
        //跟踪地址
        if(byId.getSyncCount() == null) {
            byId.setSyncCount(1);
        } else {
            byId.setSyncCount(byId.getSyncCount() + 1);
        }
        //订单状态
        truckOrdersService.saveOrUpdate(byId);

        //更新、记录订单提单信息
        truckOrderBolService.saveOrUpdate(orderBolDtoList, truckOrdersDO.getOrderCode());
        // 记录订单日志等
        log.info("更新订单提单信息，订单号: {}", truckOrdersDO.getOrderCode());
        //2、记录订单状态变更日志
        addTruckOrderLog(truckOrdersDO, byId, "change","get order bol from service");
    }

    /**
     * 记录服务商返回价格信息
     *
     * @param orderCode
     * @param orderDto
     * @return
     */
    public Boolean saveTruckOrderServiceFee(String orderCode, OrderDto orderDto,BigDecimal orderPrice) {
        TruckOrderServiceFeeDO feeDO = new TruckOrderServiceFeeDO();
        feeDO.setCurrencyCode(CurrencyEnum.USD.getValue());
        feeDO.setFeeCode(PriceRuleTypeEnum.Basic_Rate.name());
        feeDO.setOrderCode(orderCode);
        feeDO.setFeeValue(orderPrice);
        feeDO.setServiceAmount(orderDto.getPrice());
        feeDO.setTrackingNumber(orderDto.getId());
        return truckOrderServiceFeeService.save(feeDO);
    }

    /**
     * 记录订单变更日志
     *
     * @param oldOrder
     * @param newOrder
     * @return
     */
    public Boolean addTruckOrderLog(TruckOrdersDO oldOrder, TruckOrdersDO newOrder,String opType, String note) {
        TruckOrderLogDO orderLog = new TruckOrderLogDO();
        if(opType != null) {
            orderLog.setOpType(opType);
        }else {
            orderLog.setOpType("change");
        }
        orderLog.setOrderStatusFrom(oldOrder.getOrderStatus());
        orderLog.setOrderStatusTo(newOrder.getOrderStatus());
        orderLog.setOrderCode(oldOrder.getOrderCode());
        if(orderLog.getOpType().equals("submit")) {
            orderLog.setOlType(ChangeTypeEnum.STATUS.getValue());
        }else {
            orderLog.setOlType(ChangeTypeEnum.CONTENT.getValue());
        }
        Object ip = ElmOrderThreadContext.get(ElmConstant.REQUEST_IP);
        if(ip != null) {
            orderLog.setOlIp(ip.toString());
        } else {
            orderLog.setOlIp("system");
        }
        orderLog.setOrderId(newOrder.getOrderId());
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        if(loginUserId != null) {
            orderLog.setUserId(loginUserId);
            orderLog.setUserName(SecurityFrameworkUtils.getLoginUserName());
        }else {
            orderLog.setUserId(0L);
            orderLog.setUserName("system");
        }
        orderLog.setOlComments(note);
        return orderLogService.save(orderLog);
    }


    /**
     * 记录订单账单信息
     */
    public Boolean saveTruckOrderBill(TruckOrdersDO ordersDO, OrderDto orderDto, String note) {
        TruckOrderFeeDetailDO orderFeeDO = new TruckOrderFeeDetailDO();
        orderFeeDO.setCustomerCode(ordersDO.getCustomerCode());
        orderFeeDO.setProductCode(ordersDO.getProductCode());
        orderFeeDO.setSpCode(ordersDO.getSpCode());
        orderFeeDO.setReferenceNo(ordersDO.getReferenceNo());
        orderFeeDO.setCurrencyCode(CurrencyEnum.USD.getValue());
        orderFeeDO.setFtCode(PriceRuleTypeEnum.Basic_Rate.name());
        orderFeeDO.setOrderCode(ordersDO.getOrderCode());
        orderFeeDO.setOfdAmount(ordersDO.getShippingFeeEstimate());
        orderFeeDO.setTrackingNumber(orderDto.getId());
        orderFeeDO.setTrackingUrl(orderDto.getTrackingUrl());
        orderFeeDO.setOfdSysNote(note);
        orderFeeDO.setIsExtend(0);
        orderFeeDO.setIsDeduct(1);
        return orderFeeDetailService.save(orderFeeDO);
    }


    /**
     * 下单完成后计费
     * card_rate 信用卡费率,booking_fee 手续费,test_segment分段附加费
     * 计算公式： basePrice + basePrice*附件费率+ 手续费 + （basePrice + basePrice*附件费率+ 手续费 ）*信用卡费率
     * todo:说明： 由于下单后返回价格中已包含 手续费和信用卡费率，所以这里只需要计算附加费率即可
     * @param truckOrdersDO
     * @param srcPrice
     * @return
     */
    private BigDecimal calculationOrderPrice(TruckOrdersDO truckOrdersDO, BigDecimal srcPrice) {
        String productCode = truckOrdersDO.getProductCode();
        ProductInfoVO productInfo = productService.getProductInfoByCode(productCode);
        List<PriceRuleVO> baseTradingFees = productInfo.getBaseTradingFees();
//        String calculationRule = baseTradingFee.getCalculationRule();
//        BigDecimal ruleValue = baseTradingFee.getRuleValue();
        BigDecimal newPrice = srcPrice;

        PriceRuleVO surcharge = null;
        for (PriceRuleVO baseTradingFee : baseTradingFees) {
            if(!baseTradingFee.getRuleCode().equals("card_rates") && !baseTradingFee.getRuleCode().equals("booking_fee")){
                surcharge = baseTradingFee;
            }
        }
        if(surcharge != null){
            newPrice = newPrice.add(PriceCalcUtil.calculatePriceAddChargeByRule(surcharge, srcPrice));
        }else {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, "product surcharge config error:" + productCode);
        }
        return newPrice;

//        PriceRuleVO lastRule = null;
//        for (PriceRuleVO baseTradingFee : baseTradingFees) {
//            //
//            if(baseTradingFee.getRuleCode().toLowerCase().contains("card_rate")){
//                lastRule = baseTradingFee;
//                continue;
//            }
//            newPrice = newPrice.add(PriceCalcUtil.calculatePriceAddChargeByRule(baseTradingFee, srcPrice));
//        }
//        if(lastRule !=null){
//            newPrice = newPrice.add(PriceCalcUtil.calculatePriceAddChargeByRule(lastRule, newPrice));
//        }
//        return newPrice;
    }
}
